use chrono::NaiveDateTime;
use diesel::{self, prelude::*};
use serde::{Deserialize, Serialize};

use crate::schema::tasks;
use crate::schema::tasks::dsl::{
    completed as task_completed, description as task_desc, tasks as all_tasks,
};

use crate::schema::comments;
use crate::schema::comments::dsl::{
    comments as all_comments, description as comment_desc, task_id as comment_task_id,
};

#[table_name = "tasks"]
#[derive(Serialize, Identifiable, Queryable, Insertable, Debug, Clone)]
pub struct Task {
    pub id: i32,
    pub description: String,
    pub completed: bool,
    pub created_at: NaiveDateTime,
}

#[table_name = "comments"]
#[belongs_to(Task)]
#[derive(Serialize, Identifiable, Queryable, Insertable, Associations, Debug, Clone)]
pub struct Comment {
    pub id: i32,
    pub description: String,
    pub task_id: i32,
    pub created_at: NaiveDateTime,
}

// #[derive(FromForm)]
#[derive(Deserialize)]
pub struct Todo {
    pub description: String,
}

// #[derive(FromForm)]
#[derive(Deserialize)]
pub struct Comm {
    pub description: String,
}

impl Task {
    pub fn all(conn: &SqliteConnection) -> Vec<Task> {
        all_tasks
            .order(tasks::id.desc())
            .load::<Task>(conn)
            .unwrap_or(vec![])
    }

    pub fn get(id: i32, conn: &SqliteConnection) -> QueryResult<Task> {
        all_tasks.find(id).get_result::<Task>(conn)
    }

    pub fn insert(task: Todo, conn: &SqliteConnection) -> bool {
        diesel::insert_into(tasks::table)
            .values(&task_desc.eq(task.description))
            .execute(conn)
            .is_ok()
    }

    pub fn toggle_with_id(id: i32, conn: &SqliteConnection) -> bool {
        let task = all_tasks.find(id).get_result::<Task>(conn);
        if task.is_err() {
            return false;
        }

        let new_status = !task.unwrap().completed;
        let updated_task = diesel::update(all_tasks.find(id));
        updated_task
            .set(task_completed.eq(new_status))
            .execute(conn)
            .is_ok()
    }

    pub fn delete_with_id(id: i32, conn: &SqliteConnection) -> bool {
        diesel::delete(all_tasks.find(id)).execute(conn).is_ok()
    }

    pub fn delete_all(conn: &SqliteConnection) -> bool {
        diesel::delete(all_tasks).execute(conn).is_ok()
    }

    pub fn comments(task_id: i32, conn: &SqliteConnection) -> Vec<Comment> {
        all_comments
            .filter(comment_task_id.eq(task_id))
            .order(comments::id)
            .load::<Comment>(conn)
            .unwrap_or(vec![])
    }

    pub fn add_comment(task_id: i32, comm: Comm, conn: &SqliteConnection) -> bool {
        diesel::insert_into(comments::table)
            .values((
                comment_desc.eq(comm.description),
                comment_task_id.eq(task_id),
            ))
            .execute(conn)
            .is_ok()
    }

    pub fn delete_comment_with_id(id: i32, conn: &SqliteConnection) -> bool {
        diesel::delete(all_comments.find(id)).execute(conn).is_ok()
    }
}
